class Person:
    def __init__(self, first_name, last_name, age):
        self.first_name = first_name
        self.last_name = last_name
        self._age = age

    @property
    def age(self):
        return self._age

    def __str__(self):
        return f'({self.first_name},{self.last_name},{self.age})'
    
    def __repr__(self):
        return f'Person("{self.first_name}", "{self.last_name}", {self.age})'
    
    def __eq__(self, other):
        if (isinstance(other, self.__class__)):
            return self.age == other.age
        return False
    
    def __hash__(self):
        return hash(self.age)

    def __bool__(self):
        if self.age < 18 or self.age > 65:
            return False
        return True

person = Person('Coin', 'Chiang', 23)
man = Person('Coin', 'Chiang', 23)
# use str()
# __str__ calls __repr__ internally by default
# for human readable
print(person)

# use repr()
# for machine readable
print(repr(person))

print(person is man) # False, as they are not the same object
# is no __eq__ is implemented, == uses is operator
print(man == person)
print(man == 23)
print(hash(man) == hash(person))
print(bool(person))